home *** CD-ROM | disk | FTP | other *** search
- /*
- * bltGrAxis.c --
- *
- * This module implements coordinate axes for a graph
- * widget in the Tk toolkit.
- *
- * Copyright 1991-1994 by AT&T Bell Laboratories.
- * Permission to use, copy, modify, and distribute this software
- * and its documentation for any purpose and without fee is hereby
- * granted, provided that the above copyright notice appear in all
- * copies and that both that the copyright notice and warranty
- * disclaimer appear in supporting documentation, and that the
- * names of AT&T Bell Laboratories any of their entities not be used
- * in advertising or publicity pertaining to distribution of the
- * software without specific, written prior permission.
- *
- * AT&T disclaims all warranties with regard to this software, including
- * all implied warranties of merchantability and fitness. In no event
- * shall AT&T be liable for any special, indirect or consequential
- * damages or any damages whatsoever resulting from loss of use, data
- * or profits, whether in an action of contract, negligence or other
- * tortuous action, arising out of or in connection with the use or
- * performance of this software.
- *
- */
-
- #ifdef STk_CODE
- /* Define NDEBUG for Gnu Libc, to avoid the inclusion of "assert" code which
- * use "eprintf" which is not loaded in base STk. */
- # define NDEBUG
- #endif
-
- #include <assert.h>
- #include "blt.h"
- #include "bltGraph.h"
- #include "bltGrElem.h"
- #include <ctype.h>
- #include <X11/Xutil.h>
- #include <X11/Xatom.h>
-
- #define NTICK 5
-
- #ifndef SHRT_MIN
- #define SHRT_MIN -0x8000
- #endif /* SHRT_MIN */
- #ifndef SHRT_MAX
- #define SHRT_MAX 0x7FFF
- #endif /* SHRT_MAX */
-
- /*
- * Round x in terms of units
- */
- #define UROUND(x,u) (BLT_RND((x)/(u))*(u))
- #define UCEIL(x,u) (ceil((x)/(u))*(u))
- #define UFLOOR(x,u) (floor((x)/(u))*(u))
-
- #define DEF_MAJOR_TICK 0.030 /* Length of a major tick */
- #define DEF_MINOR_TICK 0.015 /* Length of a minor (sub)tick */
- #define DEF_LABEL_TICK 0.040 /* Distance from graph to start of label */
-
- #define NUMDIGITS 15 /* Specifies the number of digits of
- * accuracy used when outputting axis
- * tick labels. */
- enum PositionIndices {
- MAJOR_TICK, MINOR_TICK, TICK_LABEL, AXIS_LINE
- };
-
- #define AVG_TICK_NUM_CHARS 16 /* Assumed average tick label size */
- #define LABEL_MAX_SIZE 127 /* New buffer size to avoid crashes */
-
- enum LimitIndices {
- LMIN, LMAX
- };
-
- /* Note: The following array is ordered according to enum AxisTypes */
- static char *axisNames[] =
- {
- "x", "y", "x2", "y2"
- };
-
- /* Map normalized coordinates to window coordinates */
- #define MAPX(X,x) (BLT_RND((x)*(X)->scale)+(X)->offset)
- #define MAPY(Y,y) ((Y)->offset-BLT_RND((y)*(Y)->scale))
-
- /* Map graph coordinates to normalized coordinates [0..1] */
- #define NORM(a,x) (((x) - (a)->min) / (a)->range)
-
- /*
- * Sun's bundled and unbundled C compilers choke on static function
- * typedefs (while it can handle extern declarations) like
- *
- * static Tk_OptionParseProc parseProc;
- * static Tk_OptionPrintProc printProc;
- *
- * As a workaround, provide forward declarations here:
- */
- static int ParseAxisLimit _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, char *value, char *widgRec, int offset));
- static char *PrintAxisLimit _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin, char *widgRec, int offset, Tcl_FreeProc **freeProcPtr));
-
- static Tk_CustomOption MinLimitOption =
- {
- ParseAxisLimit, PrintAxisLimit, (ClientData)LMIN,
- };
- static Tk_CustomOption MaxLimitOption =
- {
- ParseAxisLimit, PrintAxisLimit, (ClientData)LMAX,
- };
-
- /*
- * ----------------------------------------------------------------------
- *
- * Label --
- *
- * Structure containing the formatted label and the screen
- * coordinates position of the tick label (anchored at its
- * center).
- *
- * ----------------------------------------------------------------------
- */
- typedef struct {
- char *text; /* Label for tick on axis */
- short int x, y; /* Window position of tick on graph */
- } Label;
-
- /*
- * ----------------------------------------------------------------------
- *
- * Axis --
- *
- * Structure contains options controlling how the axis will be
- * displayed.
- *
- * ----------------------------------------------------------------------
- */
-
- typedef struct {
- enum AxisTypes type; /* Type of axis: X1, Y1, X2, or Y2 */
- enum AxisLocations location;/* Location of the axis relative to plotting
- * surface: right, left, etc. */
- int logScale; /* If non-zero, scale values logarithmically */
- int mapped; /* If non-zero, display the axis */
-
- AxisDisplayProc *displayProc;
- AxisPrintProc *printProc;
- AxisLayoutProc *layoutProc;
- AxisDestroyProc *destroyProc;
-
- /* User-definable fields */
- char *title; /* Axis title */
- int showTicks; /* If non-zero, display (both major
- * and minor) ticks on the axis */
- int loose; /* If non-zero, autoscale limits loosely */
- int descending; /* In non-zero, axis values are descending
- * instead of monotonically increasing. */
- double limits[2]; /* Limits for scaling of axis */
- double prevMin, prevMax; /* Last limits for scaling of axis */
- double reqStep; /* Manually selected step size for
- * major ticks: If zero or less,
- * automatically calculate a "best"
- * step size based on range of
- * values. */
- int reqSubTicks; /* Manually selected # of subticks:
- * The default value is 2. */
- XFontStruct *fontPtr; /* Font used to draw tick labels. */
- XColor *fgColorPtr; /* Foreground color for ticks, labels,
- * and axis */
- int lineWidth; /* Line thickness of axis and ticks */
- double theta; /* Rotation of tick labels in degrees. */
- char *formatCmd; /* If non-NULL, indicates a Tcl proc
- * to call when formatting tick
- * labels. See the manual for its
- * usage. */
- double subStep; /* Step interval between minor ticks */
- int subTicks; /* # of minor ticks between major
- * ticks */
- double step; /* Step interval between major ticks */
- int numTicks; /* # of major ticks possible on axis:
- * Calculated by tick layout routines. */
-
- /* Calculated values */
- unsigned int flags;
-
- Tk_Anchor anchor; /* Anchor of tick labels */
- int posArr[4]; /* Screen location of axis, major tick,
- * minor tick, and tick label */
- XPoint titlePos; /* Coordinates of axis title */
-
- Tcl_Interp *interp;
- unsigned int width, height; /* Bounding box of axis */
-
- double tickMin, tickMax; /* Smallest and largest possible major
- * ticks on the plot */
- int tickLength; /* Legend of major tick on axis. */
- double min, max; /* Actual (including padding) axis limits */
- double range; /* Range of values (max-min) */
- double scale; /* Scale factor to convert values to
- * pixels */
- int offset; /* Offset of plotting region from window
- * origin */
-
- GC lineGC; /* Graph context for axis lines and ticks */
- GC textGC; /* Graphic context for tick labels:
- * Must be a private GC (can't use
- * Tk_GetGC) because the FillStyle,
- * TSOrigin, and Stipple fields may
- * change when the graph is layout is
- * calculated, to accommodate rotation
- * of text. Also, note that the
- * background color is also reset when
- * the background color of the graph
- * changes. */
- int numSegments; /* Size of the above segment array */
- XSegment *segArr; /* Array of computed tick line
- * segments. Also includes the axis
- * line */
- int numLabels; /* Size of the above label array */
- Label *labelArr; /* Array of computed tick labels: See the
- * description of the Label structure. */
-
- } Axis;
-
- /* Axis flags: */
- #define AXIS_CONFIG_DIRTY (1<<8)
- #define AXIS_CONFIG_USER_BIT (1<<9)
- #define AXIS_CONFIG_MIN_MASK (AXIS_CONFIG_USER_BIT << LMIN)
- #define AXIS_CONFIG_MAX_MASK (AXIS_CONFIG_USER_BIT << LMAX)
- #define AXIS_CONFIG_MIN_SET(a) ((a)->flags & AXIS_CONFIG_MIN_MASK)
- #define AXIS_CONFIG_MAX_SET(a) ((a)->flags & AXIS_CONFIG_MAX_MASK)
-
- #define VERTICAL_AXIS(a) ((a)->location&1)
- #define HORIZONTAL_AXIS(a) (!((a)->location&1))
-
- #define DEF_AXIS_ALT_MAPPED "false"
- #define DEF_AXIS_COMMAND (char *)NULL
- #define DEF_AXIS_DESCENDING "0"
- #define DEF_AXIS_FG_COLOR BLACK
- #define DEF_AXIS_FG_MONO BLACK
- #define DEF_AXIS_FONT "-*-Courier-Medium-R-Normal--*-100-*-*-*-*-*-*"
- #define DEF_AXIS_TICK_LENGTH "0.1i"
- #define DEF_AXIS_LINE_WIDTH "0"
- #define DEF_AXIS_LOG_SCALE "0"
- #define DEF_AXIS_LOOSE "0"
- #define DEF_AXIS_MAX (char *)NULL
- #define DEF_AXIS_MIN (char *)NULL
- #define DEF_AXIS_ROTATE "0.0"
- #define DEF_AXIS_STD_MAPPED "1"
- #define DEF_AXIS_STEPSIZE "0.0"
- #define DEF_AXIS_SUBTICKS "2"
- #define DEF_AXIS_TICKS "1"
- #define DEF_AXIS_X_STEPSIZE_BARCHART "1.0"
- #define DEF_AXIS_X_SUBTICKS_BARCHART "0"
- #define DEF_AXIS_TITLE (char *)NULL
-
- static Tk_ConfigSpec xAxisConfigSpecs[] =
- {
- {TK_CONFIG_COLOR, "-color", "xColor", "AxisColor",
- DEF_AXIS_FG_COLOR, Tk_Offset(Axis, fgColorPtr),
- TK_CONFIG_COLOR_ONLY | ALL_MASK},
- {TK_CONFIG_COLOR, "-color", "xColor", "AxisColor",
- DEF_AXIS_FG_MONO, Tk_Offset(Axis, fgColorPtr),
- TK_CONFIG_MONO_ONLY | ALL_MASK},
- {TK_CONFIG_STRING, "-command", "xCommand", "AxisCommand",
- DEF_AXIS_COMMAND, Tk_Offset(Axis, formatCmd),
- ALL_MASK | TK_CONFIG_NULL_OK},
- {TK_CONFIG_BOOLEAN, "-descending", "xDescending", "AxisDescending",
- DEF_AXIS_DESCENDING, Tk_Offset(Axis, descending),
- TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
- {TK_CONFIG_FONT, "-font", "xFont", "AxisFont",
- DEF_AXIS_FONT, Tk_Offset(Axis, fontPtr), ALL_MASK},
- {TK_CONFIG_PIXELS, "-linewidth", "xLinewidth", "AxisLinewidth",
- DEF_AXIS_LINE_WIDTH, Tk_Offset(Axis, lineWidth),
- TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
- {TK_CONFIG_BOOLEAN, "-logscale", "xLogscale", "AxisLogscale",
- DEF_AXIS_LOG_SCALE, Tk_Offset(Axis, logScale),
- TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
- {TK_CONFIG_BOOLEAN, "-loose", "xLoose", "AxisLoose",
- DEF_AXIS_LOOSE, Tk_Offset(Axis, loose),
- TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
- {TK_CONFIG_BOOLEAN, "-mapped", "xMapped", "AxisMapped",
- DEF_AXIS_STD_MAPPED, Tk_Offset(Axis, mapped),
- TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
- {TK_CONFIG_CUSTOM, "-max", "xMax", "AxisMax",
- DEF_AXIS_MAX, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MaxLimitOption},
- {TK_CONFIG_CUSTOM, "-min", "xMin", "AxisMin",
- DEF_AXIS_MIN, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MinLimitOption},
- {TK_CONFIG_DOUBLE, "-rotate", "xRotate", "AxisRotate",
- DEF_AXIS_ROTATE, Tk_Offset(Axis, theta),
- TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
- {TK_CONFIG_BOOLEAN, "-showticks", "xShowticks", "AxisShowticks",
- DEF_AXIS_TICKS, Tk_Offset(Axis, showTicks),
- TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
- {TK_CONFIG_DOUBLE, "-stepsize", "xStepsize", "AxisStepsize",
- DEF_AXIS_STEPSIZE, Tk_Offset(Axis, reqStep), XYGRAPH_MASK},
- {TK_CONFIG_DOUBLE, "-stepsize", "xStepsize", "AxisStepsize",
- DEF_AXIS_X_STEPSIZE_BARCHART, Tk_Offset(Axis, reqStep),
- BARCHART_MASK},
- {TK_CONFIG_INT, "-subticks", "xSubticks", "AxisSubticks",
- DEF_AXIS_SUBTICKS, Tk_Offset(Axis, reqSubTicks), XYGRAPH_MASK},
- {TK_CONFIG_INT, "-subticks", "xSubticks", "AxisSubticks",
- DEF_AXIS_X_SUBTICKS_BARCHART, Tk_Offset(Axis, reqSubTicks),
- BARCHART_MASK},
- {TK_CONFIG_PIXELS, "-ticklength", "xTickLength", "AxisTickLength",
- DEF_AXIS_TICK_LENGTH, Tk_Offset(Axis, tickLength), ALL_MASK},
- {TK_CONFIG_STRING, "-title", "xTitle", "AxisTitle",
- "X", Tk_Offset(Axis, title), ALL_MASK | TK_CONFIG_NULL_OK},
- {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
- };
-
-
- static Tk_ConfigSpec x2AxisConfigSpecs[] =
- {
- {TK_CONFIG_COLOR, "-color", "x2Color", "AxisColor",
- DEF_AXIS_FG_COLOR, Tk_Offset(Axis, fgColorPtr),
- TK_CONFIG_COLOR_ONLY | ALL_MASK},
- {TK_CONFIG_COLOR, "-color", "x2Color", "AxisColor",
- DEF_AXIS_FG_MONO, Tk_Offset(Axis, fgColorPtr),
- TK_CONFIG_MONO_ONLY | ALL_MASK},
- {TK_CONFIG_STRING, "-command", "x2Command", "AxisCommand",
- DEF_AXIS_COMMAND, Tk_Offset(Axis, formatCmd),
- ALL_MASK | TK_CONFIG_NULL_OK},
- {TK_CONFIG_BOOLEAN, "-descending", "x2Descending", "AxisDescending",
- DEF_AXIS_DESCENDING, Tk_Offset(Axis, descending),
- TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
- {TK_CONFIG_FONT, "-font", "x2Font", "AxisFont",
- DEF_AXIS_FONT, Tk_Offset(Axis, fontPtr), ALL_MASK},
- {TK_CONFIG_PIXELS, "-linewidth", "x2Linewidth", "AxisLinewidth",
- DEF_AXIS_LINE_WIDTH, Tk_Offset(Axis, lineWidth),
- TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
- {TK_CONFIG_BOOLEAN, "-logscale", "x2Logscale", "AxisLogscale",
- DEF_AXIS_LOG_SCALE, Tk_Offset(Axis, logScale),
- TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
- {TK_CONFIG_BOOLEAN, "-loose", "x2Loose", "AxisLoose",
- DEF_AXIS_LOOSE, Tk_Offset(Axis, loose),
- TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
- {TK_CONFIG_BOOLEAN, "-mapped", "x2Mapped", "AxisMapped",
- DEF_AXIS_ALT_MAPPED, Tk_Offset(Axis, mapped),
- TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
- {TK_CONFIG_CUSTOM, "-max", "x2Max", "AxisMax",
- DEF_AXIS_MAX, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MaxLimitOption},
- {TK_CONFIG_CUSTOM, "-min", "x2Min", "AxisMin",
- DEF_AXIS_MIN, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MinLimitOption},
- {TK_CONFIG_DOUBLE, "-rotate", "x2Rotate", "AxisRotate",
- DEF_AXIS_ROTATE, Tk_Offset(Axis, theta),
- TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
- {TK_CONFIG_BOOLEAN, "-showticks", "x2Showticks", "AxisShowticks",
- DEF_AXIS_TICKS, Tk_Offset(Axis, showTicks), ALL_MASK},
- {TK_CONFIG_DOUBLE, "-stepsize", "x2Stepsize", "AxisStepsize",
- DEF_AXIS_STEPSIZE, Tk_Offset(Axis, reqStep),
- TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
- {TK_CONFIG_INT, "-subticks", "x2Subticks", "AxisSubticks",
- DEF_AXIS_SUBTICKS, Tk_Offset(Axis, reqSubTicks),
- TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
- {TK_CONFIG_PIXELS, "-ticklength", "x2TickLength", "AxisTickLength",
- DEF_AXIS_TICK_LENGTH, Tk_Offset(Axis, tickLength), ALL_MASK},
- {TK_CONFIG_STRING, "-title", "x2Title", "AxisTitle",
- DEF_AXIS_TITLE, Tk_Offset(Axis, title), ALL_MASK | TK_CONFIG_NULL_OK},
- {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
- };
-
- static Tk_ConfigSpec yAxisConfigSpecs[] =
- {
- {TK_CONFIG_COLOR, "-color", "yColor", "AxisColor",
- DEF_AXIS_FG_COLOR, Tk_Offset(Axis, fgColorPtr),
- TK_CONFIG_COLOR_ONLY | ALL_MASK},
- {TK_CONFIG_COLOR, "-color", "yColor", "AxisColor",
- DEF_AXIS_FG_MONO, Tk_Offset(Axis, fgColorPtr),
- TK_CONFIG_MONO_ONLY | ALL_MASK},
- {TK_CONFIG_STRING, "-command", "yCommand", "AxisCommand",
- (char *)NULL, Tk_Offset(Axis, formatCmd),
- ALL_MASK | TK_CONFIG_NULL_OK},
- {TK_CONFIG_BOOLEAN, "-descending", "yDescending", "AxisDescending",
- DEF_AXIS_DESCENDING, Tk_Offset(Axis, descending),
- TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
- {TK_CONFIG_FONT, "-font", "yFont", "AxisFont",
- DEF_AXIS_FONT, Tk_Offset(Axis, fontPtr), ALL_MASK},
- {TK_CONFIG_BOOLEAN, "-loose", "yLoose", "AxisLoose",
- DEF_AXIS_LOOSE, Tk_Offset(Axis, loose),
- TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
- {TK_CONFIG_PIXELS, "-linewidth", "yLinewidth", "AxisLinewidth",
- DEF_AXIS_LINE_WIDTH, Tk_Offset(Axis, lineWidth),
- TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
- {TK_CONFIG_BOOLEAN, "-logscale", "yLogscale", "AxisLogscale",
- DEF_AXIS_LOG_SCALE, Tk_Offset(Axis, logScale),
- TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
- {TK_CONFIG_BOOLEAN, "-mapped", "yMapped", "AxisMapped",
- DEF_AXIS_STD_MAPPED, Tk_Offset(Axis, mapped),
- TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
- {TK_CONFIG_CUSTOM, "-max", "yMax", "AxisMax",
- DEF_AXIS_MAX, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MaxLimitOption},
- {TK_CONFIG_CUSTOM, "-min", "yMin", "AxisMin",
- DEF_AXIS_MIN, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MinLimitOption},
- {TK_CONFIG_DOUBLE, "-rotate", "yRotate", "AxisRotate",
- DEF_AXIS_ROTATE, Tk_Offset(Axis, theta),
- TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
- {TK_CONFIG_BOOLEAN, "-showticks", "yShowticks", "AxisShowticks",
- DEF_AXIS_TICKS, Tk_Offset(Axis, showTicks),
- TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
- {TK_CONFIG_DOUBLE, "-stepsize", "yStepsize", "AxisStepsize",
- DEF_AXIS_STEPSIZE, Tk_Offset(Axis, reqStep),
- TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
- {TK_CONFIG_INT, "-subticks", "ySubticks", "AxisSubticks",
- DEF_AXIS_SUBTICKS, Tk_Offset(Axis, reqSubTicks), ALL_MASK},
- {TK_CONFIG_PIXELS, "-ticklength", "yTickLength", "AxisTickLength",
- DEF_AXIS_TICK_LENGTH, Tk_Offset(Axis, tickLength), ALL_MASK},
- {TK_CONFIG_STRING, "-title", "yTitle", "AxisTitle",
- "Y", Tk_Offset(Axis, title), ALL_MASK | TK_CONFIG_NULL_OK},
- {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
- };
-
- static Tk_ConfigSpec y2AxisConfigSpecs[] =
- {
- {TK_CONFIG_COLOR, "-color", "y2Color", "AxisColor",
- DEF_AXIS_FG_COLOR, Tk_Offset(Axis, fgColorPtr),
- TK_CONFIG_COLOR_ONLY | ALL_MASK},
- {TK_CONFIG_COLOR, "-color", "y2Color", "AxisColor",
- DEF_AXIS_FG_MONO, Tk_Offset(Axis, fgColorPtr),
- TK_CONFIG_MONO_ONLY | ALL_MASK},
- {TK_CONFIG_STRING, "-command", "y2Command", "AxisCommand",
- DEF_AXIS_COMMAND, Tk_Offset(Axis, formatCmd),
- ALL_MASK | TK_CONFIG_NULL_OK},
- {TK_CONFIG_BOOLEAN, "-descending", "y2Descending", "AxisDescending",
- DEF_AXIS_DESCENDING, Tk_Offset(Axis, descending),
- TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
- {TK_CONFIG_FONT, "-font", "y2Font", "AxisFont",
- DEF_AXIS_FONT, Tk_Offset(Axis, fontPtr), ALL_MASK},
- {TK_CONFIG_PIXELS, "-linewidth", "y2Linewidth", "AxisLinewidth",
- DEF_AXIS_LINE_WIDTH, Tk_Offset(Axis, lineWidth),
- TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
- {TK_CONFIG_BOOLEAN, "-loose", "y2Loose", "AxisLoose",
- DEF_AXIS_LOOSE, Tk_Offset(Axis, loose),
- TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
- {TK_CONFIG_BOOLEAN, "-logscale", "y2Logscale", "AxisLogscale",
- DEF_AXIS_LOG_SCALE, Tk_Offset(Axis, logScale),
- TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
- {TK_CONFIG_BOOLEAN, "-mapped", "y2Mapped", "AxisMapped",
- DEF_AXIS_ALT_MAPPED, Tk_Offset(Axis, mapped),
- TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
- {TK_CONFIG_CUSTOM, "-max", "y2Max", "AxisMax",
- DEF_AXIS_MAX, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MaxLimitOption},
- {TK_CONFIG_CUSTOM, "-min", "y2Min", "AxisMin",
- DEF_AXIS_MIN, 0, ALL_MASK | TK_CONFIG_NULL_OK, &MinLimitOption},
- {TK_CONFIG_DOUBLE, "-rotate", "y2Rotate", "AxisRotate",
- DEF_AXIS_ROTATE, Tk_Offset(Axis, theta),
- TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
- {TK_CONFIG_BOOLEAN, "-showticks", "y2Showticks", "AxisShowticks",
- DEF_AXIS_TICKS, Tk_Offset(Axis, showTicks),
- TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
- {TK_CONFIG_DOUBLE, "-stepsize", "y2Stepsize", "AxisStepsize",
- DEF_AXIS_STEPSIZE, Tk_Offset(Axis, reqStep),
- TK_CONFIG_DONT_SET_DEFAULT | ALL_MASK},
- {TK_CONFIG_INT, "-subticks", "y2Subticks", "AxisSubticks",
- DEF_AXIS_SUBTICKS, Tk_Offset(Axis, reqSubTicks), ALL_MASK},
- {TK_CONFIG_PIXELS, "-ticklength", "y2TickLength", "AxisTickLength",
- DEF_AXIS_TICK_LENGTH, Tk_Offset(Axis, tickLength), ALL_MASK},
- {TK_CONFIG_STRING, "-title", "y2Title", "AxisTitle",
- DEF_AXIS_TITLE, Tk_Offset(Axis, title), ALL_MASK | TK_CONFIG_NULL_OK},
- {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
- };
-
- static Tk_ConfigSpec *axisConfigSpecs[4] =
- {
- xAxisConfigSpecs, yAxisConfigSpecs, /* Normal x-axis and y-axis */
- x2AxisConfigSpecs, y2AxisConfigSpecs /* Alternate x-axis and y-axis */
- };
-
- /* ----------------------------------------------------------------------
- * Custom option parse and print procedures
- * ----------------------------------------------------------------------
- */
- /*
- * ----------------------------------------------------------------------
- *
- * ParseAxisLimit --
- *
- * Convert the string representation of an axis limit into its
- * numeric form.
- *
- * Results:
- * The return value is a standard Tcl result. The symbol type is
- * written into the widget record.
- *
- * ----------------------------------------------------------------------
- */
- /*ARGSUSED*/
- static int
- ParseAxisLimit(clientData, interp, tkwin, value, widgRec, offset)
- ClientData clientData; /* Either LMIN or LMAX */
- Tcl_Interp *interp; /* Interpreter to send results back to */
- Tk_Window tkwin; /* not used */
- char *value; /* */
- char *widgRec; /* Axis structure */
- int offset; /* Offset of limit */
- {
- Axis *axisPtr = (Axis *)(widgRec);
- int whichLimit = (int)clientData;
- unsigned int mask;
-
- mask = (AXIS_CONFIG_USER_BIT << whichLimit);
- if ((value == NULL) || (*value == '\0')) {
- axisPtr->flags &= ~mask;
- } else {
- double newLimit;
-
- if (Tcl_ExprDouble(interp, value, &newLimit) != TCL_OK) {
- return TCL_ERROR;
- }
- axisPtr->limits[whichLimit] = newLimit;
- axisPtr->flags |= mask;
- }
- return TCL_OK;
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * PrintAxisLimit --
- *
- * Convert the floating point axis limit into a string.
- *
- * Results:
- * The string representation of the limit is returned.
- *
- * ----------------------------------------------------------------------
- */
- /*ARGSUSED*/
- static char *
- PrintAxisLimit(clientData, tkwin, widgRec, offset, freeProcPtr)
- ClientData clientData; /* Either LMIN or LMAX */
- Tk_Window tkwin; /* not used */
- char *widgRec; /* */
- int offset;
- Tcl_FreeProc **freeProcPtr;
- {
- Axis *axisPtr = (Axis *)(widgRec);
- int whichLimit = (int)clientData;
- unsigned int mask;
- char *result;
-
- result = "";
- mask = (AXIS_CONFIG_USER_BIT << whichLimit);
- if (axisPtr->flags & mask) {
- char string[TCL_DOUBLE_SPACE + 1];
-
- Tcl_PrintDouble(axisPtr->interp, axisPtr->limits[whichLimit],
- string);
- result = strdup(string);
- if (result == NULL) {
- return "";
- }
- *freeProcPtr = (Tcl_FreeProc *)free;
- }
- return result;
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * MakeLabel --
- *
- * Converts a floating point tick value to a string representation.
- *
- * Results:
- * Returns a formatted label in the string buffer.
- *
- * Side Effects:
- * Formatted tick label will be displayed on the graph.
- *
- * ----------------------------------------------------------------------
- */
- static int
- MakeLabel(graphPtr, axisPtr, value, string)
- Graph *graphPtr; /* Graph widget */
- Axis *axisPtr; /* Axis structure */
- double value; /* */
- char string[]; /* string (length is always
- * LABEL_MAX_SIZE+1) containing the
- * formatted label */
- {
- if (axisPtr->logScale) {
- sprintf(string, "1E%d", BLT_RND(value));
- } else {
- if (axisPtr->formatCmd == NULL) {
- sprintf(string, "%.*g", NUMDIGITS, value);
- } else {
- Tcl_PrintDouble(axisPtr->interp, value, string);
- }
- }
- if (axisPtr->formatCmd != NULL) {
- Tcl_ResetResult(axisPtr->interp);
- if (Tcl_VarEval(axisPtr->interp, axisPtr->formatCmd, " ",
- Tk_PathName(graphPtr->tkwin), " ",
- string, (char *)NULL) != TCL_OK) {
- Tk_BackgroundError(axisPtr->interp);
- } else {
- char *result;
-
- result = axisPtr->interp->result;
- if (*result != '\0') {
- strncpy(string, result, LABEL_MAX_SIZE);
- string[LABEL_MAX_SIZE] = 0;
- Tcl_ResetResult(axisPtr->interp);
- }
- }
- }
- return TCL_OK;
- }
-
- /* Map graph coordinate to normalized coordinates (consider log scale) */
- static double
- Scale(axisPtr, x)
- Axis *axisPtr;
- double x;
- {
- if (x == Blt_posInfinity) {
- return (1.0);
- } else if (x == Blt_negInfinity) {
- return (0.0);
- }
- if (axisPtr->logScale) {
- if (x > 0.0) {
- x = log10(x);
- } else if (x < 0.0) {
- x = 0.0;
- }
- }
- return (NORM(axisPtr, x));
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * Blt_InvTransform --
- *
- * Maps the given window y-coordinate back to a graph coordinate
- * value. Called by the graph locater routine.
- *
- * Results:
- * Returns the graph coordinate value at the given window
- * y-coordinate.
- *
- * ----------------------------------------------------------------------
- */
- double
- Blt_InvTransform(axis, coord)
- GraphAxis *axis;
- int coord;
- {
- double norm, value;
- Axis *axisPtr = (Axis *)axis;
-
- if (HORIZONTAL_AXIS(axisPtr)) {
- coord = (coord - axisPtr->offset);
- } else {
- coord = (axisPtr->offset - coord);
- }
- norm = ((double)coord / axisPtr->scale);
- if (axisPtr->descending) {
- norm = 1.0 - norm;
- }
- value = (norm * axisPtr->range) + axisPtr->min;
- if (axisPtr->logScale) {
- value = BLT_EXP10(value);
- }
- return (value);
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * Blt_Transform --
- *
- * Map the given graph coordinate value to its axis, returning a
- * window position.
- *
- * Results:
- * Returns the window coordinate position on the given axis.
- *
- * Note:
- * Since line and polygon clipping is performed by the X server,
- * we must be careful about coordinates which are outside of the
- * range of a signed short int.
- *
- * ----------------------------------------------------------------------
- */
- int
- Blt_Transform(axis, value)
- GraphAxis *axis;
- double value;
- {
- Axis *axisPtr = (Axis *)axis;
- double norm;
- int coord;
-
- norm = Scale(axisPtr, value);
- if (axisPtr->descending) {
- norm = 1.0 - norm;
- }
- coord = HORIZONTAL_AXIS(axisPtr)
- ? MAPX(axisPtr, norm) : MAPY(axisPtr, norm);
-
- /* Should really figure out a good offset value and test for that
- * because we could still generate bogus numbers */
- if (coord >= SHRT_MAX) {
- coord = SHRT_MAX - 1000;
- } else if (coord <= SHRT_MIN) {
- coord = SHRT_MIN + 1000;
- }
- return (coord);
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * Blt_TransformPt --
- *
- * Maps the given graph x,y coordinate values to a window position.
- *
- * Results:
- * Returns a XPoint structure containing the window coordinates of
- * the given graph x,y coordinate.
- *
- * ----------------------------------------------------------------------
- */
- XPoint
- Blt_TransformPt(graphPtr, x, y, axisFlags)
- Graph *graphPtr;
- double x, y;
- unsigned int axisFlags; /* Specifies which axes to use */
- {
- XPoint winPos;
- enum AxisTypes axisType;
-
- axisType = (axisFlags & X1_AXIS_MASK) ? X1_AXIS : X2_AXIS;
- winPos.x = Blt_Transform(graphPtr->axisArr[axisType], x);
- axisType = (axisFlags & Y1_AXIS_MASK) ? Y1_AXIS : Y2_AXIS;
- winPos.y = Blt_Transform(graphPtr->axisArr[axisType], y);
- if (graphPtr->inverted) { /* Swap x and y coordinates */
- int coord;
-
- coord = winPos.y;
- winPos.y = winPos.x;
- winPos.x = coord;
- }
- return (winPos);
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * Blt_TransformDist --
- *
- * Map the given graph x-coordinate value to a window position.
- *
- * Results:
- * Returns the window coordinate position at the given graph
- * x-coordinate.
- *
- * Note:
- * Since line and polygon clipping is performed by the X server,
- * we must be careful about coordinates which are outside of the
- * range of a signed short int.
- *
- * ----------------------------------------------------------------------
- */
- int
- Blt_TransformDist(axis, value)
- GraphAxis *axis;
- double value;
- {
- Axis *axisPtr = (Axis *)axis;
- double norm;
- int zero, coord;
-
- norm = Scale(axisPtr, 0.0);
- zero = HORIZONTAL_AXIS(axisPtr)
- ? MAPX(axisPtr, norm) : MAPY(axisPtr, norm);
- norm = Scale(axisPtr, value);
- coord = HORIZONTAL_AXIS(axisPtr)
- ? MAPX(axisPtr, norm) : MAPY(axisPtr, norm);
- coord -= zero;
- return (BLT_ABS(coord));
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * Blt_PointOnGraph --
- *
- * Determines if the window coordinates given represent a point
- * on the graph (within the bounds of the graph axes).
- *
- * Results:
- * Returns 1 is the point is within the bounds of the graph,
- * 0 otherwise.
- *
- * ----------------------------------------------------------------------
- */
- int
- Blt_PointOnGraph(graphPtr, pointPtr)
- Graph *graphPtr;
- XPoint *pointPtr;
- {
- double norm;
- Axis *axisPtr;
-
- /*
- * It doesn't make a difference which x-axis or y-axis we use
- * (the point is referenced by window coordinates). We're just
- * checking if the point's within the range of axis' normalized
- * coordinates [0..1].
- */
- axisPtr = (Axis *)graphPtr->bottomAxis;
- if (axisPtr->scale == 0.0) {
- return 0; /* Axis layout hasn't been calculated yet */
- }
- norm = (pointPtr->x - axisPtr->offset) / axisPtr->scale;
- if ((norm < 0.0) || (norm > 1.0)) {
- return 0; /* x-coordinates are off the graph */
- }
- axisPtr = (Axis *)graphPtr->leftAxis;
- norm = (axisPtr->offset - pointPtr->y) / axisPtr->scale;
- return ((norm >= 0.0) && (norm <= 1.0));
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * UpdateLimits --
- *
- * Updates the min and max values for each axis as determined by
- * the data elements currently to be displayed.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * Minimum, maximum data limit fields for both the X and Y axes
- * in the graph widget record are updated.
- *
- * ----------------------------------------------------------------------
- */
- static void
- UpdateLimits(graphPtr, axisPtr)
- Graph *graphPtr;
- Axis *axisPtr;
- {
- int result;
-
- if ((!AXIS_CONFIG_MAX_SET(axisPtr)) || (!AXIS_CONFIG_MIN_SET(axisPtr))) {
- register Element *elemPtr;
- Blt_ListEntry *entryPtr;
- register double min, max;
- double elemMin, elemMax;
- double value;
-
- /* Find the minimum and maximum values for all the elements
- displayed */
- min = Blt_posInfinity, max = Blt_negInfinity;
- for (entryPtr = Blt_FirstListEntry(&(graphPtr->elemList));
- entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
- elemPtr = (Element *)Blt_GetListValue(entryPtr);
- if ((*elemPtr->limitsProc) (graphPtr, elemPtr,
- (GraphAxis *)axisPtr, &elemMin, &elemMax) > 0) {
- if (min > elemMin) {
- min = elemMin;
- }
- if (max < elemMax) {
- max = elemMax;
- }
- }
- }
- /*
- * When auto-scaling, the axis limits are the bounds of the
- * element data. If no data exists, set arbitrary limits (wrt
- * to log/linear scale).
- */
- if (min == Blt_posInfinity) {
- min = (axisPtr->logScale) ? 0.001 : -10.0;
- }
- if (max == Blt_negInfinity) {
- max = 10.0;
- }
- /*
- * Handle situations where only one limit is set.
- */
- value = min;
- if (AXIS_CONFIG_MIN_SET(axisPtr)) {
- min = value = axisPtr->limits[LMIN];
- } else if (AXIS_CONFIG_MAX_SET(axisPtr)) {
- max = value = axisPtr->limits[LMAX];
- }
- /* If there's no range of data (min >= max), manufacture one */
- if (min >= max) {
- if (value == 0.0) {
- min = -0.1, max = 0.1;
- } else {
- double x;
-
- x = BLT_FABS(value) * 0.1;
- min = value - x;
- max = value + x;
- }
- }
- if (!AXIS_CONFIG_MIN_SET(axisPtr)) {
- axisPtr->limits[LMIN] = min;
- }
- if (!AXIS_CONFIG_MAX_SET(axisPtr)) {
- axisPtr->limits[LMAX] = max;
- }
- }
- /* Indicate if the axis limits have changed */
- result = (axisPtr->limits[LMAX] != axisPtr->prevMax) ||
- (axisPtr->limits[LMIN] != axisPtr->prevMin);
- /* and save the previous minimum and maximum values */
- axisPtr->prevMin = axisPtr->limits[LMIN];
- axisPtr->prevMax = axisPtr->limits[LMAX];
- if (result) {
- axisPtr->flags |= AXIS_CONFIG_DIRTY;
- }
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * NiceNum --
- *
- * Taken from Paul Heckbert's "Nice Numbers for Graph Labels" in
- * Graphics Gems (pp 61-63). Finds a "nice" number approximately
- * equal to x. Round the number if round=1, take ceiling if round=0.
- *
- * ----------------------------------------------------------------------
- */
- static double
- NiceNum(x, round)
- double x;
- int round;
- {
- double exponX; /* exponent of x */
- double fractX; /* fractional part of x */
- double nf; /* nice, rounded fraction */
-
- exponX = floor(log10(x));
- fractX = x / BLT_EXP10(exponX); /* between 1 and 10 */
- if (round) {
- if (fractX < 1.5) {
- nf = 1.;
- } else if (fractX < 3.0) {
- nf = 2.;
- } else if (fractX < 7.0) {
- nf = 5.;
- } else {
- nf = 10.;
- }
- } else if (fractX <= 1.0) {
- nf = 1.;
- } else if (fractX <= 2.0) {
- nf = 2.;
- } else if (fractX <= 5.0) {
- nf = 5.0;
- } else {
- nf = 10.0;
- }
- return (nf * BLT_EXP10(exponX));
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * LogAxis --
- *
- * Determine the range and units of a log scaled axis.
- *
- * Unless the axis limits are specified, the axis is scaled
- * automatically, where the smallest and largest major ticks
- * encompass the range of actual data values. When an axis
- * limit is specified, that value represents the
- * smallest(min)/largest(max) value in the displayed range of
- * values.
- *
- * Both manual and automatic scaling are affected by the
- * step used. By default, the step is the largest
- * power of ten to divide the range in more than one piece.
- *
- * Automatic scaling:
- * Find the smallest number of units which contain the range of
- * values. The minimum and maximum major tick values will be
- * represent the range of values for the axis. This greatest
- * number of major ticks possible is 10.
- *
- * Manual scaling:
- * Make the minimum and maximum data values the represent the
- * range of the values for the axis. The minimum and maximum
- * major ticks will be inclusive of this range. This provides
- * the largest area for plotting and the expected results when
- * the axis min and max values have be set by the user (.e.g zooming).
- * The maximum number of major ticks is 20.
- *
- * For log scale, there is always the possibility that the minimum
- * and maximum data values are the same magnitude. To represent
- * the points properly, at least one full decade should be shown.
- * However, if you zoom a log scale plot, the results should be
- * predictable. Therefore, in that case, show only minor ticks.
- * Lastly, there should be an appropriate way to handle numbers <=0.
- *
- * maxY
- * | units = magnitude (of least significant digit)
- * | high = largest unit tick < max axis value
- * high _| low = smallest unit tick > min axis value
- * |
- * | range = high - low
- * | # ticks = greatest factor of range/units
- * _|
- * U |
- * n |
- * i |
- * t _|
- * |
- * |
- * |
- * low _|
- * |
- * |_minX________________maxX__
- * | | | | |
- * minY low high
- * minY
- *
- *
- * numTicks = Number of ticks
- * min = Minimum value of axis
- * max = Maximum value of axis
- * range = Range of values (max - min)
- *
- * If the number of decades is greater than ten, it is assumed
- * that the full set of log-style ticks can't be drawn properly.
- *
- * Results:
- * None
- *
- * ----------------------------------------------------------------------
- */
- static void
- LogAxis(axisPtr)
- Axis *axisPtr;
- {
- double range;
- double min, max;
-
- min = axisPtr->limits[LMIN];
- max = axisPtr->limits[LMAX];
-
- if (min > 0.0) {
- min = floor(log10(min));
- } else {
- min = 0.0;
- }
- if (max > 0.0) {
- max = ceil(log10(max));
- } else {
- max = 1.0;
- }
- range = max - min;
- if (range > 10) {
- range = NiceNum(range, 0);
- axisPtr->step = NiceNum(range / (NTICK - 1), 1);
-
- /* Find the outer limits in terms of the step. */
- min = UFLOOR(min, axisPtr->step);
- max = UCEIL(max, axisPtr->step);
- axisPtr->numTicks = (int)((max - min) / axisPtr->step) + 1;
- axisPtr->subStep = BLT_EXP10(floor(log10(axisPtr->step)));
-
- if (axisPtr->step == axisPtr->subStep) {
- axisPtr->subTicks = 5;
- axisPtr->subStep = axisPtr->step * 0.2;
- } else {
- axisPtr->subTicks = BLT_RND(axisPtr->step / axisPtr->subStep);
- }
- } else {
- if (min == max) {
- max++;
- }
- axisPtr->numTicks = (int)((max - min) + 1);
- axisPtr->step = 1.0;
- axisPtr->subTicks = 10;
- }
- axisPtr->min = axisPtr->tickMin = min;
- axisPtr->max = axisPtr->tickMax = max;
- axisPtr->range = (max - min);
- #ifdef notdef
- fprintf(stderr, "Major: %s\nRegion min=%g,max=%g\nTick min=%g,max=%g\n\
- numTicks=%d, range=%g, step=%.15g\n", axisNames[axisPtr->type], min, max,
- axisPtr->tickMin, axisPtr->tickMax, axisPtr->numTicks,
- axisPtr->range, axisPtr->step);
- fprintf(stderr, "Minor numTicks=%d, step=%.15g\n\n",
- axisPtr->subTicks, axisPtr->subStep);
- #endif
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * LinearAxis --
- *
- * Determine the units of a linear scaled axis.
- *
- * Unless the axis limits are specified, the axis is scaled
- * automatically, where the smallest and largest major ticks
- * encompass the range of actual data values. When an axis
- * limit is specified, that value represents the
- * smallest(min)/largest(max) value in the displayed range of
- * values.
- *
- * Both manual and automatic scaling are affected by the
- * step used. By default, the step is the largest
- * power of ten to divide the range in more than one piece.
- *
- * Automatic scaling:
- * Find the smallest number of units which contain the range of
- * values. The minimum and maximum major tick values will be
- * represent the range of values for the axis. This greatest
- * number of major ticks possible is 10.
- *
- * Manual scaling:
- * Make the minimum and maximum data values the represent the
- * range of the values for the axis. The minimum and maximum
- * major ticks will be inclusive of this range. This provides
- * the largest area for plotting and the expected results when
- * the axis min and max values have be set by the user (.e.g zooming).
- * The maximum number of major ticks is 20.
- *
- * For log scale, there is always the possibility that the minimum
- * and maximum data values are the same magnitude. To represent
- * the points properly, at least one full decade should be shown.
- * However, if you zoom a log scale plot, the results should be
- * predictable. Therefore, in that case, show only minor ticks.
- * Lastly, there should be an appropriate way to handle numbers <=0.
- *
- * maxY
- * | units = magnitude (of least significant digit)
- * | high = largest unit tick < max axis value
- * high _| low = smallest unit tick > min axis value
- * |
- * | range = high - low
- * | # ticks = greatest factor of range/units
- * _|
- * U |
- * n |
- * i |
- * t _|
- * |
- * |
- * |
- * low _|
- * |
- * |_minX________________maxX__
- * | | | | |
- * minY low high
- * minY
- *
- *
- * numTicks = Number of ticks
- * min = Minimum value of axis
- * max = Maximum value of axis
- * range = Range of values (max - min)
- *
- * Results:
- * None.
- *
- * ----------------------------------------------------------------------
- */
- static void
- LinearAxis(axisPtr)
- Axis *axisPtr;
- {
- double range, unit, pad;
- double min, max;
-
- min = axisPtr->limits[LMIN];
- max = axisPtr->limits[LMAX];
-
- /*
- * Calculate the major step.
- */
- range = max - min;
-
- if ((axisPtr->reqStep > 0.0) && (axisPtr->reqStep < range)) {
- axisPtr->step = axisPtr->reqStep;
- } else {
- range = NiceNum(range, 0);
- axisPtr->step = NiceNum(range / (NTICK - 1), 1);
- }
-
- /*
- * Find the outer tick values in terms of the major step interval.
- * Add +0.0 to preclude the possibility of an IEEE -0.0.
- */
-
- axisPtr->tickMin = UFLOOR(min, axisPtr->step) + 0.0;
- axisPtr->tickMax = UCEIL(max, axisPtr->step) + 0.0;
- range = axisPtr->tickMax - axisPtr->tickMin;
- unit = range / axisPtr->step;
- axisPtr->numTicks = BLT_RND(unit) + 1;
-
- /*
- * If the axis is "loose", the range is between the two outermost
- * ticks. Otherwise if it's "tight", the range is between the data
- * min and max.
- */
- if (axisPtr->loose) {
- axisPtr->min = axisPtr->tickMin, axisPtr->max = axisPtr->tickMax;
- } else {
- axisPtr->min = min, axisPtr->max = max;
- }
-
- /*
- * If is a limit is auto-scaled, add some padding so that the
- * symbols representing data points at the extremes aren't clipped
- * in half by the edge of the plot. Two percent is an arbitrary
- * guess.
- */
-
- pad = (axisPtr->max - axisPtr->min) * 0.02;
- if (!AXIS_CONFIG_MIN_SET(axisPtr)) {
- axisPtr->min -= pad;
- }
- if (!AXIS_CONFIG_MAX_SET(axisPtr)) {
- axisPtr->max += pad;
- }
- axisPtr->range = axisPtr->max - axisPtr->min;
-
- #ifdef notdef
- fprintf(stderr, "Major: %s\nRegion min=%g,max=%g\nTick min=%g,max=%g\n\
- numTicks=%d, range=%g, step=%.15g\n", axisNames[axisPtr->type], min, max,
- axisPtr->tickMin, axisPtr->tickMax, axisPtr->numTicks,
- axisPtr->range, axisPtr->step);
- #endif
-
- /* Now calculate the minor tick step and number. */
- axisPtr->subTicks = axisPtr->reqSubTicks;
- if (axisPtr->subTicks < 0) {
- axisPtr->subTicks = 0;
- }
- if (axisPtr->subTicks > 0) {
- axisPtr->subStep = axisPtr->step / axisPtr->subTicks;
- } else {
- axisPtr->subStep = axisPtr->step * 0.2; /* Need this for layout */
- }
- #ifdef notdef
- fprintf(stderr, "Minor numTicks=%d, step=%.15g\n\n",
- axisPtr->subTicks, axisPtr->subStep);
- #endif
- }
-
- /*
- * -----------------------------------------------------------------
- *
- * SetAxisLimits --
- *
- * -----------------------------------------------------------------
- */
- static void
- SetAxisLimits(graphPtr, axisPtr)
- Graph *graphPtr;
- Axis *axisPtr;
- {
- UpdateLimits(graphPtr, axisPtr);
- /*
- * For barcharts, adjust the min or max values to include 0.0
- * if the bars are drawn along this axis.
- */
- if ((graphPtr->type == BARCHART) && (Y_AXIS(axisPtr))) {
- if (!AXIS_CONFIG_MIN_SET(axisPtr) && (axisPtr->limits[LMIN] > 0.0)) {
- axisPtr->limits[LMIN] = 0.0;
- }
- if (!AXIS_CONFIG_MAX_SET(axisPtr) && (axisPtr->limits[LMAX] < 0.0)) {
- axisPtr->limits[LMAX] = 0.0;
- }
- }
- if (axisPtr->flags & AXIS_CONFIG_DIRTY) {
- /* Calculate min/max tick (major/minor) layouts */
- if (axisPtr->logScale) {
- LogAxis(axisPtr);
- } else {
- LinearAxis(axisPtr);
- }
- axisPtr->flags &= ~AXIS_CONFIG_DIRTY;
-
- /* When any axis changes, we need to layout the entire graph. */
- graphPtr->flags |= (LAYOUT_ALL | DIRTY | REFRESH);
- }
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * Blt_ComputeAxes --
- *
- * Results:
- * None.
- *
- * ----------------------------------------------------------------------
- */
- void
- Blt_ComputeAxes(graphPtr)
- Graph *graphPtr;
- {
- register int i;
-
- for (i = 0; i < 4; i++) {
- SetAxisLimits(graphPtr, (Axis *)graphPtr->axisArr[i]);
- }
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * ConfigureAxis --
- *
- * Configures axis attributes (font, line width, label, etc) and
- * allocates a new (possibly shared) graphics context. Line cap
- * style is projecting. This is for the problem of when a tick
- * sits directly at the end point of the axis.
- *
- * Results:
- * The return value is a standard Tcl result.
- *
- * Side Effects:
- * Axis resources are allocated (GC, font). Axis layout is
- * deferred until the height and width of the window are known.
- *
- * ----------------------------------------------------------------------
- */
- static int
- ConfigureAxis(graphPtr, axisPtr, argc, argv, flags)
- Graph *graphPtr;
- Axis *axisPtr;
- int argc;
- char *argv[];
- int flags;
- {
- GC newGC;
- XGCValues gcValues;
- unsigned long gcMask;
- Tk_ConfigSpec *configSpecs;
-
- configSpecs = axisConfigSpecs[axisPtr->type];
- if (flags & TK_CONFIG_ARGV_ONLY) {
- if (argc == 0) {
- return (Tk_ConfigureInfo(graphPtr->interp, graphPtr->tkwin,
- configSpecs, (char *)axisPtr, (char *)NULL, flags));
- } else if (argc == 1) {
- return (Tk_ConfigureInfo(graphPtr->interp, graphPtr->tkwin,
- configSpecs, (char *)axisPtr, argv[0], flags));
- }
- }
- if (Tk_ConfigureWidget(graphPtr->interp, graphPtr->tkwin, configSpecs,
- argc, argv, (char *)axisPtr, flags) != TCL_OK) {
- return TCL_ERROR;
- }
- /*
- * Check requested X and Y axis limits. Can't allow min to be
- * greater than max, or have undefined log scale limits.
- */
- if ((AXIS_CONFIG_MIN_SET(axisPtr)) && (AXIS_CONFIG_MAX_SET(axisPtr)) &&
- (axisPtr->limits[LMIN] >= axisPtr->limits[LMAX])) {
- sprintf(graphPtr->interp->result,
- "impossible %s-axis limits (min %g >= max %g)",
- axisNames[axisPtr->type], axisPtr->limits[LMIN],
- axisPtr->limits[LMAX]);
- return TCL_ERROR;
- }
- if ((axisPtr->logScale) && (AXIS_CONFIG_MIN_SET(axisPtr)) &&
- (axisPtr->limits[LMIN] <= 0.0)) {
- sprintf(graphPtr->interp->result,
- "invalid %s-axis limits (min=%g,max=%g) for log scale",
- axisNames[axisPtr->type], axisPtr->limits[LMIN],
- axisPtr->limits[LMAX]);
- return TCL_ERROR;
- }
- /*
- * Reset bogus line widths to zero. Can't allow bad line widths
- * because the layout routines compute axis and tick positions
- * with them.
- */
- if (axisPtr->lineWidth < 1) {
- axisPtr->lineWidth = 0;
- }
- /*
- * Create an unshared GC for the tick labels. The GC is private
- * because the labels may be rotated, requiring the GCStipple and
- * GCTSOffset fields to change.
- */
- gcMask = GCForeground | GCFont;
- gcValues.font = axisPtr->fontPtr->fid;
- gcValues.foreground = axisPtr->fgColorPtr->pixel;
- if (graphPtr->border != NULL) {
- gcValues.background = Tk_3DBorderColor(graphPtr->border)->pixel;
- gcMask |= GCBackground;
- }
- newGC = XCreateGC(graphPtr->display, Tk_WindowId(graphPtr->tkwin),
- gcMask, &gcValues);
- if (axisPtr->textGC != NULL) {
- XFreeGC(graphPtr->display, axisPtr->textGC);
- }
- axisPtr->textGC = newGC;
-
- /* Create GC for axis line and ticks. */
-
- gcMask = GCForeground | GCLineWidth | GCCapStyle;
- gcValues.line_width = axisPtr->lineWidth;
- gcValues.cap_style = CapProjecting;
- newGC = Tk_GetGC(graphPtr->tkwin, gcMask, &gcValues);
- if (axisPtr->lineGC != NULL) {
- Tk_FreeGC(graphPtr->display, axisPtr->lineGC);
- }
- axisPtr->lineGC = newGC;
-
- /*
- * Don't bother to check what options have changed. Almost every
- * axis configuration option changes the size of the plotting area
- * (except for -foreground).
- */
- graphPtr->flags |= LAYOUT_ALL;
- axisPtr->flags |= AXIS_CONFIG_DIRTY;
- SetAxisLimits(graphPtr, axisPtr);
- Blt_RedrawGraph(graphPtr);
- return TCL_OK;
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * DestroyAxis --
- *
- * Results:
- * None.
- *
- * Side effects:
- * Resources (font, color, gc, labels, etc.) associated with the
- * axis are deallocated.
- *
- * ----------------------------------------------------------------------
- */
- static void
- DestroyAxis(graphPtr, axis)
- Graph *graphPtr;
- GraphAxis *axis;
- {
- Axis *axisPtr = (Axis *)axis;
-
- Tk_FreeOptions(axisConfigSpecs[axisPtr->type], (char *)axisPtr,
- graphPtr->display, 0);
-
- if (axisPtr->lineGC != NULL) {
- Tk_FreeGC(graphPtr->display, axisPtr->lineGC);
- }
- if (axisPtr->textGC != NULL) {
- XFreeGC(graphPtr->display, axisPtr->textGC);
- }
- if (axisPtr->labelArr != NULL) {
- free((char *)axisPtr->labelArr);
- }
- if (axisPtr->segArr != NULL) {
- free((char *)axisPtr->segArr);
- }
- free((char *)axisPtr);
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * CalculateOffsets --
- *
- * Determines the placements of the axis, major and minor ticks,
- * and title of the axis.
- *
- * Results:
- * None.
- *
- * ----------------------------------------------------------------------
- */
- static void
- CalculateOffsets(graphPtr, axisPtr)
- Graph *graphPtr;
- Axis *axisPtr;
- {
- int pad; /* Offset of axis from interior region. This
- * includes a possible border and the axis
- * line width. */
- unsigned int textHeight;
- int innerPos, outerPos;
- int majorOffset, minorOffset, labelOffset, titleOffset;
-
- textHeight = TEXTHEIGHT(graphPtr->fontPtr);
-
- titleOffset = graphPtr->borderWidth + textHeight;
- majorOffset = BLT_ABS(axisPtr->tickLength);
- minorOffset = BLT_RND(majorOffset * 0.5);
- labelOffset = BLT_RND(majorOffset * 1.4) + axisPtr->lineWidth / 2;
-
- /* Adjust offset for the interior border width and the line width */
- pad = graphPtr->plotBW + axisPtr->lineWidth + 2;
- if (graphPtr->plotBW > 0) {
- pad++;
- }
- if ((axisPtr->location == LEFT_AXIS) || (axisPtr->location == TOP_AXIS)) {
- majorOffset = -majorOffset;
- minorOffset = -minorOffset;
- labelOffset = -labelOffset;
- }
- /*
- * Pre-calculate the x-coordinate positions of the axis,
- * tick labels, and the individual major and minor ticks.
- */
- switch (axisPtr->location) {
- case BOTTOM_AXIS:
- innerPos = graphPtr->origin.y + pad;
- axisPtr->titlePos.x = (graphPtr->extreme.x + graphPtr->origin.x) / 2;
- axisPtr->titlePos.y = graphPtr->height - titleOffset;
- axisPtr->anchor = TK_ANCHOR_N;
- break;
-
- case LEFT_AXIS:
- innerPos = graphPtr->origin.x - pad;
- axisPtr->titlePos.x = titleOffset;
- axisPtr->titlePos.y = (graphPtr->origin.y + graphPtr->extreme.y) / 2;
- axisPtr->anchor = TK_ANCHOR_E;
- break;
-
- case TOP_AXIS:
- innerPos = graphPtr->extreme.y - pad;
- axisPtr->titlePos.x = (graphPtr->extreme.x + graphPtr->origin.x) / 2;
- axisPtr->titlePos.y = titleOffset;
- if (graphPtr->title != NULL) {
- axisPtr->titlePos.y += (2 * textHeight);
- } else {
- axisPtr->titlePos.y += (textHeight / 2);
- }
- axisPtr->anchor = TK_ANCHOR_S;
- break;
-
- case RIGHT_AXIS:
- innerPos = graphPtr->extreme.x + pad;
- axisPtr->titlePos.x = graphPtr->width -
- (graphPtr->legendPtr->width + titleOffset);
- axisPtr->titlePos.y = (graphPtr->origin.y + graphPtr->extreme.y) / 2;
- axisPtr->anchor = TK_ANCHOR_W;
- break;
-
- default:
- abort();
- }
- outerPos = innerPos + majorOffset;
- axisPtr->posArr[MAJOR_TICK] = outerPos;
- axisPtr->posArr[AXIS_LINE] = innerPos;
- axisPtr->posArr[MINOR_TICK] = innerPos + minorOffset;
- axisPtr->posArr[TICK_LABEL] = innerPos + labelOffset;
- if (axisPtr->tickLength < 0) {
- axisPtr->posArr[MAJOR_TICK] = innerPos;
- axisPtr->posArr[AXIS_LINE] = outerPos;
- }
- }
-
- static XSegment
- AxisLine(axisPtr, min, max)
- Axis *axisPtr; /* Axis information */
- double min, max; /* Limits of axis in graph coordinates */
- {
- double normMin, normMax;
- XSegment segment;
-
- normMax = NORM(axisPtr, min);
- if (axisPtr->descending) {
- normMax = 1.0 - normMax;
- }
- normMin = NORM(axisPtr, max);
- if (axisPtr->descending) {
- normMin = 1.0 - normMin;
- }
- if (HORIZONTAL_AXIS(axisPtr)) {
- segment.y1 = segment.y2 = axisPtr->posArr[AXIS_LINE];
- segment.x1 = MAPX(axisPtr, normMin);
- segment.x2 = MAPX(axisPtr, normMax);
- } else {
- segment.x1 = segment.x2 = axisPtr->posArr[AXIS_LINE];
- segment.y1 = MAPY(axisPtr, normMin);
- segment.y2 = MAPY(axisPtr, normMax);
- }
- return (segment);
- }
-
-
- static XSegment
- Tick(axisPtr, value, whichTick)
- Axis *axisPtr;
- double value;
- int whichTick; /* If non-zero, create minor tick instead */
- {
- double norm;
- XSegment segment;
- int tick;
-
- norm = NORM(axisPtr, value);
- if (axisPtr->descending) {
- norm = 1.0 - norm;
- }
- tick = axisPtr->posArr[whichTick];
- if (HORIZONTAL_AXIS(axisPtr)) {
- segment.y1 = axisPtr->posArr[AXIS_LINE];
- segment.y2 = tick;
- segment.x1 = segment.x2 = MAPX(axisPtr, norm);
- } else {
- segment.x1 = axisPtr->posArr[AXIS_LINE];
- segment.x2 = tick;
- segment.y1 = segment.y2 = MAPY(axisPtr, norm);
- }
- return (segment);
- }
-
- /*
- * -----------------------------------------------------------------
- *
- * LayoutAxis --
- *
- * Pre-calculate the x-coordinate positions of the axis, ticks
- * and labels to be used later when displaying the X axis. Ticks
- * (minor and major) will be saved in an array of XSegments so
- * that they can be drawn in one XDrawSegments call. The strings
- * representing the tick labels and the corresponding window
- * positions are saved in an array of Label's.
- *
- * Calculates the values for each major and minor tick and checks to
- * see if they are in range (the outer ticks may be outside of the
- * range of plotted values).
- *
- * Results:
- * None.
- *
- * SideEffects:
- * Line segments and tick labels saved will be used to draw
- * the X axis.
- * ----------------------------------------------------------------- */
- static void
- LayoutAxis(graphPtr, axis)
- Graph *graphPtr;
- GraphAxis *axis;
- {
- Axis *axisPtr = (Axis *)axis;
- XSegment *segArr;
- unsigned int arraySize;
- double min, max;
- double value, subValue;
- register int i, j;
- register int sgmts, labels;
- double epsilon;
- static float logTable[] = /* Precomputed log10 values [1..10] */
- {
- 0.0, 0.301, 0.477, 0.602, 0.699, 0.778, 0.845, 0.903, 0.954, 1.0
- };
-
- CalculateOffsets(graphPtr, axisPtr);
-
- /* Save all line coordinates in an array of line segments. */
-
- arraySize = (1 + (axisPtr->numTicks * (axisPtr->subTicks + 1)));
- segArr = (XSegment *)malloc(arraySize * sizeof(XSegment));
- if (segArr == NULL) {
- return; /* Can't allocate array of segments */
- }
- if ((axisPtr->logScale) || (axisPtr->loose) ||
- (axisPtr->limits[LMIN] == axisPtr->limits[LMAX])) {
- min = axisPtr->tickMin, max = axisPtr->tickMax;
- } else {
- min = axisPtr->limits[LMIN];
- max = axisPtr->limits[LMAX];
- }
-
- /* Axis baseline */
- segArr[0] = AxisLine(axisPtr, min, max);
-
- sgmts = 1, labels = 0;
- if (!axisPtr->showTicks) {
- goto done; /* Only display axis line */
- }
- /* Use numbers just beyond the limits when testing for equality */
- epsilon = 2 * MIN_DBL_VALUE;
- min -= epsilon, max += epsilon;
-
- value = axisPtr->tickMin; /* Start from smallest axis tick */
- for (i = 0; i < axisPtr->numTicks; i++) {
- subValue = value = UROUND(value, axisPtr->step);
-
- /* Minor ticks */
- for (j = 1; j < axisPtr->subTicks; j++) {
- if ((axisPtr->logScale) && (axisPtr->step == 1.0)) {
- subValue = value + (double)logTable[j];
- } else {
- subValue += axisPtr->subStep;
- }
- if ((subValue >= min) && (subValue <= max)) {
- segArr[sgmts] = Tick(axisPtr, subValue, MINOR_TICK);
- sgmts++;
- }
- }
-
- /* Major tick and label */
- if ((value >= min) && (value <= max)) {
- short int labelPos;
-
- segArr[sgmts] = Tick(axisPtr, value, MAJOR_TICK);
- labelPos = (short int)axisPtr->posArr[TICK_LABEL];
-
- /* Save tick label position */
-
- if (HORIZONTAL_AXIS(axisPtr)) {
- axisPtr->labelArr[labels].x = segArr[sgmts].x1;
- axisPtr->labelArr[labels].y = labelPos;
- } else {
- axisPtr->labelArr[labels].x = labelPos;
- axisPtr->labelArr[labels].y = segArr[sgmts].y1;
- }
- sgmts++, labels++;
- }
- value += axisPtr->step;
- }
-
- done:
-
- assert(sgmts <= arraySize);
- assert(labels <= axisPtr->numLabels);
-
- if (axisPtr->segArr != NULL) {
- free((char *)axisPtr->segArr);
- }
- axisPtr->segArr = segArr;
- axisPtr->numSegments = sgmts;
- }
-
- /*
- * -----------------------------------------------------------------
- *
- * DisplayAxis --
- *
- * Draws the axis, ticks, and labels onto the canvas.
- *
- * Initializes and passes text attribute information through
- * TextAttributes structure.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * Axis gets drawn on window.
- *
- * -----------------------------------------------------------------
- */
-
- static float titleRot[4] = /* Rotation for each axis title */
- {
- 0.0, 90.0, 0.0, 270.0
- };
-
- static void
- DisplayAxis(graphPtr, axis, attrPtr)
- Graph *graphPtr;
- GraphAxis *axis;
- TextAttributes *attrPtr;
- {
- Axis *axisPtr = (Axis *)axis;
-
- if (axisPtr->title != NULL) {
- attrPtr->theta = (double)titleRot[axisPtr->location];
- Blt_DrawText(graphPtr->display, graphPtr->canvas, axisPtr->title,
- attrPtr, axisPtr->titlePos.x, axisPtr->titlePos.y);
- }
- if (axisPtr->showTicks) {
- register int i;
- TextAttributes textAttr;
-
- /* Setup static text attribute information */
-
- textAttr.theta = axisPtr->theta;
- textAttr.anchor = axisPtr->anchor;
- textAttr.fontPtr = axisPtr->fontPtr;
- textAttr.fgColorPtr = axisPtr->fgColorPtr;
- textAttr.bgColorPtr = Tk_3DBorderColor(graphPtr->border);
- textAttr.gc = axisPtr->textGC;
-
- /* Draw the ticks labels and then the ticks and axis */
- for (i = 0; i < axisPtr->numLabels; i++) {
- Blt_DrawText(graphPtr->display, graphPtr->canvas,
- axisPtr->labelArr[i].text, &textAttr,
- axisPtr->labelArr[i].x, axisPtr->labelArr[i].y);
- }
- }
- if (axisPtr->numSegments > 0) {
- XDrawSegments(graphPtr->display, graphPtr->canvas, axisPtr->lineGC,
- axisPtr->segArr, axisPtr->numSegments);
- }
- }
-
- /*
- * -----------------------------------------------------------------
- *
- * PrintAxis --
- *
- * Generates PostScript output to draw the axis, ticks, and
- * labels.
- *
- * Initializes and passes text attribute information through
- * TextAttributes structure.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * PostScript output is left in graphPtr->interp->result;
- *
- * -----------------------------------------------------------------
- */
- static void
- PrintAxis(graphPtr, axis, attrPtr)
- Graph *graphPtr;
- GraphAxis *axis;
- TextAttributes *attrPtr;
- {
- Axis *axisPtr = (Axis *)axis;
-
- if (axisPtr->title != NULL) {
- attrPtr->theta = (double)titleRot[axisPtr->location];
- Blt_TextToPostScript(graphPtr, axisPtr->title, attrPtr,
- axisPtr->titlePos.x, axisPtr->titlePos.y);
- }
- if (axisPtr->showTicks) {
- TextAttributes textAttr;
- register int i;
-
- /* Setup static text attribute information */
-
- textAttr.theta = axisPtr->theta;
- textAttr.anchor = axisPtr->anchor;
- textAttr.fontPtr = axisPtr->fontPtr;
- textAttr.fgColorPtr = axisPtr->fgColorPtr;
- textAttr.bgColorPtr = (XColor *)NULL;
-
- for (i = 0; i < axisPtr->numLabels; i++) {
- Blt_TextToPostScript(graphPtr, axisPtr->labelArr[i].text,
- &textAttr, axisPtr->labelArr[i].x, axisPtr->labelArr[i].y);
- }
- }
- if (axisPtr->numSegments > 0) {
- Blt_SetLineAttributes(graphPtr, axisPtr->fgColorPtr,
- axisPtr->lineWidth, 0);
- Blt_SegmentsToPostScript(graphPtr, axisPtr->segArr,
- axisPtr->numSegments);
- }
- }
-
- static void
- GetAxisGeometry(graphPtr, axisPtr)
- Graph *graphPtr;
- Axis *axisPtr;
- {
- register int i;
- register int count;
- char label[LABEL_MAX_SIZE+1];
- unsigned int length;
- unsigned int arraySize, poolSize, used;
- char *pool;
- unsigned int textWidth, textHeight;
- unsigned int bbWidth, bbHeight;
- int maxWidth, maxHeight;
- double value;
- double epsilon, minAxis, maxAxis;
- Label *labelArr;
- int pad;
-
- if ((axisPtr->logScale) || (axisPtr->loose) ||
- (axisPtr->limits[LMIN] == axisPtr->limits[LMAX])) {
- minAxis = axisPtr->tickMin, maxAxis = axisPtr->tickMax;
- } else {
- minAxis = axisPtr->limits[LMIN];
- maxAxis = axisPtr->limits[LMAX];
- }
-
- /* Use numbers just beyond the limits when testing for equality */
-
- epsilon = 2 * MIN_DBL_VALUE;
- minAxis -= epsilon, maxAxis += epsilon;
-
- /* Create an array of labels with an attached of characters strings */
- arraySize = axisPtr->numTicks * sizeof(Label);
- poolSize = axisPtr->numTicks * AVG_TICK_NUM_CHARS * sizeof(char);
- labelArr = (Label *)malloc(arraySize + poolSize);
- pool = (char *)labelArr + arraySize;
-
- textHeight = TEXTHEIGHT(axisPtr->fontPtr);
-
- maxHeight = maxWidth = 0;
- used = count = 0;
- value = axisPtr->tickMin;
- for (i = 0; i < axisPtr->numTicks; i++, value += axisPtr->step) {
- value = UROUND(value, axisPtr->step);
- if ((value < minAxis) || (value > maxAxis)) {
- continue; /* Out of range */
- }
- MakeLabel(graphPtr, axisPtr, value, label);
- length = strlen(label);
-
- /* Resize the label array if we overflow its string pool */
- if (poolSize <= (used + length + 1)) {
- int newSize;
- Label *newArr;
-
- newSize = poolSize + poolSize;
- while (newSize <= (used + length + 1)) {
- newSize += newSize;
- }
- newArr = (Label *)malloc(arraySize + newSize);
- memcpy((char *)newArr, (char *)labelArr, arraySize + poolSize);
- pool = (char *)newArr + arraySize;
- free((char *)labelArr);
- poolSize = newSize;
- labelArr = newArr;
- }
- textWidth = Blt_TextStringWidth(axisPtr->fontPtr, label);
- labelArr[count].text = (pool + used);
- strcpy(labelArr[count].text, label);
- used += (length + 1);
- count++;
-
- if (axisPtr->theta == 0.0) {
- bbWidth = textWidth, bbHeight = textHeight;
- } else {
- Blt_GetBoundingBox(textWidth, textHeight, axisPtr->theta,
- &bbWidth, &bbHeight, (XPoint *)NULL);
- }
- if (bbWidth > maxWidth) {
- maxWidth = bbWidth;
- }
- if (bbHeight > maxHeight) {
- maxHeight = bbHeight;
- }
- }
- if (axisPtr->labelArr != NULL) {
- free((char *)axisPtr->labelArr);
- }
- axisPtr->labelArr = labelArr;
- axisPtr->numLabels = count;
- assert(axisPtr->numLabels <= axisPtr->numTicks);
-
- /*
- * Because the axis cap style is "CapProjecting", there's an extra
- * 1.5 linewidth to be accounted for.
- */
- pad = ((axisPtr->lineWidth * 15) / 10) + 2;
- axisPtr->width = maxWidth + pad;
- axisPtr->height = maxHeight + pad;
-
- value = BLT_ABS(axisPtr->tickLength) * 1.4;
- pad = BLT_RND(value) + graphPtr->plotBW + 1;
- if (graphPtr->plotBW > 0) {
- pad++;
- }
- if (HORIZONTAL_AXIS(axisPtr)) {
- axisPtr->height += pad;
- } else {
- axisPtr->width += pad;
- }
- }
-
- void
- Blt_UpdateAxisBackgrounds(graphPtr, colorPtr)
- Graph *graphPtr;
- XColor *colorPtr; /* Background color of graph margin area */
- {
- Axis *axisPtr;
- register int i;
-
- for (i = 0; i < 4; i++) {
- axisPtr = (Axis *)graphPtr->axisArr[i];
- XSetBackground(Tk_Display(graphPtr->tkwin), axisPtr->textGC,
- colorPtr->pixel);
- }
- }
-
- /*
- * -----------------------------------------------------------------
- *
- * Blt_ComputeLayout --
- *
- * Calculate the layout of the graph. Based upon the data,
- * axis limits, X and Y titles, and title height, determine
- * the cavity left which is the plotting surface. The first
- * step get the data and axis limits for calculating the space
- * needed for the top, bottom, left, and right margins.
- *
- * 1) The LEFT margin is the area from the left border to the
- * Y axis (not including ticks). It composes the border
- * width, the width an optional Y axis label and its padding,
- * and the tick numeric labels. The Y axis label is rotated
- * 90 degrees so that the width is the font height.
- *
- * 2) The RIGHT margin is the area from the end of the graph
- * to the right window border. It composes the border width,
- * some padding, the font height (this may be dubious. It
- * appears to provide a more even border), the max of the
- * legend width and 1/2 max X tick number. This last part is
- * so that the last tick label is not clipped.
- *
- * Area Width
- * ___________________________________________________________
- * | | | |
- * | | TOP height of title | |
- * | | | |
- * | | x2 title | |
- * | | | |
- * | | height of x2-axis | |
- * |__________|_______________________________|_______________| A
- * | | extreme| | r
- * | | | | e
- * | LEFT | | RIGHT | a
- * | | | |
- * | y | Free area = 104% | y2 | H
- * | | Plotting surface = 100% | | e
- * | t | Tick length = 2 + 2% | t | i
- * | i | | i | g
- * | t | | t legend| h
- * | l | | l width| t
- * | e | | e |
- * | height| |height |
- * | of | | of |
- * | y-axis| |y2-axis |
- * | | | |
- * | |origin | |
- * |__________|_______________________________|_______________|
- * | | (xoffset, yoffset) | |
- * | | | |
- * | | height of x-axis | |
- * | | | |
- * | | BOTTOM x title | |
- * |__________|_______________________________|_______________|
- *
- * 3) The TOP margin is the area from the top window border to the top
- * of the graph. It composes the border width, twice the height of
- * the title font (if one is given) and some padding between the
- * title.
- *
- * 4) The BOTTOM margin is area from the bottom window border to the
- * X axis (not including ticks). It composes the border width, the height
- * an optional X axis label and its padding, the height of the font
- * of the tick labels.
- *
- * The plotting area is between the margins which includes the X and Y axes
- * including the ticks but not the tick numeric labels. The length of
- * the ticks and its padding is 5% of the entire plotting area. Hence the
- * entire plotting area is scaled as 105% of the width and height of the
- * area.
- *
- * The axis labels, ticks labels, title, and legend may or may not be
- * displayed which must be taken into account.
- *
- *
- * -----------------------------------------------------------------
- */
- int
- Blt_ComputeLayout(graphPtr)
- Graph *graphPtr;
- {
- int left, right, top, bottom;
- int maxTickWidth;
- int height;
- int leftOver;
- unsigned int borderWidths;
- unsigned int lineHeight = TEXTHEIGHT(graphPtr->fontPtr);
- Axis *x1, *x2, *y1, *y2;
- unsigned int twiceHeight = (2 * lineHeight);
- unsigned int halfHeight = (lineHeight / 2);
- double value;
-
- x1 = (Axis *)graphPtr->bottomAxis;
- x2 = (Axis *)graphPtr->topAxis;
- y1 = (Axis *)graphPtr->leftAxis;
- y2 = (Axis *)graphPtr->rightAxis;
-
- top = (graphPtr->title != NULL) ? twiceHeight : halfHeight;
- left = ((y1->mapped) && (y1->title != NULL)) ? twiceHeight : halfHeight;
- bottom = ((x1->mapped) && (x1->title != NULL)) ? twiceHeight : halfHeight;
- right = ((y2->mapped) && (y2->title != NULL)) ? twiceHeight : 0;
-
- if ((x2->mapped) && (x2->title != NULL)) {
- top += twiceHeight;
- }
- GetAxisGeometry(graphPtr, x1);
- maxTickWidth = 0;
- if ((x1->mapped) && (x1->showTicks)) {
- bottom += x1->height;
- }
- GetAxisGeometry(graphPtr, x2);
- if ((x2->mapped) && (x2->showTicks)) {
- top += x2->height;
- }
- GetAxisGeometry(graphPtr, y1);
- if ((y1->mapped) && (y1->showTicks)) {
- left += y1->width + PADX;
- }
- GetAxisGeometry(graphPtr, y2);
- if ((y2->mapped) && (y2->showTicks)) {
- right += y2->width + PADX;
- }
- /* Override calculated values if user specified margins */
-
- if (graphPtr->leftMargin > 0) {
- left = graphPtr->leftMargin;
- }
- if (graphPtr->topMargin > 0) {
- top = graphPtr->topMargin;
- }
- if (graphPtr->bottomMargin > 0) {
- bottom = graphPtr->bottomMargin;
- }
- borderWidths = graphPtr->borderWidth + graphPtr->plotBW;
- height = graphPtr->height - ((2 * borderWidths) + top + bottom);
- (*graphPtr->legendPtr->geomProc) (graphPtr, height);
- if ((graphPtr->legendPtr->mapped) && (graphPtr->legendPtr->useDefault)) {
- right += graphPtr->legendPtr->width;
- } else {
- right += halfHeight;
- }
- maxTickWidth = BLT_MAX(x1->width, x2->width) / 2;
- if (right < maxTickWidth) {
- right = maxTickWidth;
- }
- if (graphPtr->rightMargin > 0) {
- right = graphPtr->rightMargin;
- }
- top += borderWidths;
- left += borderWidths;
- right += borderWidths;
- bottom += borderWidths;
-
- /* Based upon the margins, calculate the space left for the graph. */
-
- x1->offset = left;
- y1->offset = graphPtr->height - bottom;
- leftOver = graphPtr->width - (left + right);
- if (leftOver < 0) {
- return TCL_ERROR;
- }
- x1->scale = (double)leftOver; /* Pixels per X unit */
- leftOver = graphPtr->height - (top + bottom);
- if (leftOver < 0) {
- return TCL_ERROR;
- }
- y1->scale = (double)leftOver; /* Pixels per Y unit */
- x2->scale = x1->scale;
- x2->offset = x1->offset;
- y2->scale = y1->scale;
- y2->offset = y1->offset;
-
- /* Calculate the average symbol (formula is arbitrary) */
-
- value = log(((double)x1->scale) * y1->scale) * 0.8;
- graphPtr->avgSymSize = BLT_RND(value);
-
- graphPtr->origin.x = MAPX(x1, 0.0);
- graphPtr->origin.y = MAPY(y1, 0.0);
- graphPtr->extreme.x = MAPX(x1, 1.0);
- graphPtr->extreme.y = MAPY(y1, 1.0);
- return TCL_OK;
- }
-
- /*
- *--------------------------------------------------------------
- *
- * GetAxisLimits --
- *
- * This procedure returns a string representing the axis limits
- * of the graph. The format of the string is { xmin ymin xmax ymax}.
- *
- * Results:
- * Always returns TCL_OK. The interp->result field is
- * a list of the graph axis limits.
- *
- *--------------------------------------------------------------
- */
- static int
- GetAxisLimits(axisPtr, argc, argv)
- Axis *axisPtr;
- int argc;
- char **argv;
-
- {
- char string[TCL_DOUBLE_SPACE + 1];
- double min, max;
-
- if (argc != 3) {
- Tcl_AppendResult(axisPtr->interp, "wrong # args: should be \"",
- argv[0], " ", axisNames[axisPtr->type], "axis limits\"",
- (char *)NULL);
- return TCL_ERROR;
- }
- min = axisPtr->min, max = axisPtr->max;
- if (axisPtr->logScale) {
- min = BLT_EXP10(min);
- max = BLT_EXP10(max);
- }
- Tcl_PrintDouble(axisPtr->interp, min, string);
- Tcl_AppendElement(axisPtr->interp, string);
- Tcl_PrintDouble(axisPtr->interp, max, string);
- Tcl_AppendElement(axisPtr->interp, string);
- return TCL_OK;
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * InvTransformCoord --
- *
- * Maps the given window coordinate into an axis-value.
- *
- * Results:
- * Returns a standard Tcl result. interp->result contains
- * the axis value. If an error occurred, TCL_ERROR is returned
- * and interp->result will contain an error message.
- *
- * ----------------------------------------------------------------------
- */
- static int
- InvTransformCoord(axisPtr, argc, argv)
- Axis *axisPtr;
- int argc;
- char **argv;
- {
- int coord; /* Integer window coordinate*/
- char string[TCL_DOUBLE_SPACE + 1];
- double value;
-
- if (argc != 4) {
- Tcl_AppendResult(axisPtr->interp, "wrong # args: should be \"",
- argv[0], " ", axisNames[axisPtr->type],
- "axis invtransform winPos\"", (char *)NULL);
- return TCL_ERROR;
- }
- if (Tcl_GetInt(axisPtr->interp, argv[2], &coord) != TCL_OK) {
- return TCL_ERROR;
- }
- value = Blt_InvTransform((GraphAxis *)axisPtr, coord);
- Tcl_PrintDouble(axisPtr->interp, value, string);
- Tcl_AppendElement(axisPtr->interp, string);
- return TCL_OK;
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * TransformCoord --
- *
- * Maps the given axis-value to a window coordinate.
- *
- * Results:
- * Returns a standard Tcl result. interp->result contains
- * the window coordinate. If an error occurred, TCL_ERROR
- * is returned and interp->result will contain an error
- * message.
- *
- * ----------------------------------------------------------------------
- */
- static int
- TransformCoord(axisPtr, argc, argv)
- Axis *axisPtr; /* Axis */
- int argc;
- char **argv;
- {
- double value;
- int coord;
-
- if (argc != 4) {
- Tcl_AppendResult(axisPtr->interp, "wrong # args: should be \"",
- argv[0], " ", axisNames[axisPtr->type], "axis transform value\"",
- (char *)NULL);
- return TCL_ERROR;
- }
- if (Tcl_ExprDouble(axisPtr->interp, argv[2], &value) != TCL_OK) {
- return TCL_ERROR;
- }
- coord = Blt_Transform((GraphAxis *)axisPtr, value);
- sprintf(axisPtr->interp->result, "%d", coord);
- return TCL_OK;
- }
-
- /*
- * ----------------------------------------------------------------------
- *
- * Blt_CreateAxis --
- *
- * Create and initialize a structure containing information to
- * display a graph axis.
- *
- * Results:
- * The return value is a standard Tcl result.
- *
- * ----------------------------------------------------------------------
- */
- int
- Blt_CreateAxis(graphPtr, type, flags)
- Graph *graphPtr;
- enum AxisTypes type;
- int flags; /* Configuration flags */
- {
- Axis *axisPtr;
- enum AxisLocations location;
-
- axisPtr = (Axis *)calloc(1, sizeof(Axis));
- if (axisPtr == NULL) {
- graphPtr->interp->result = "can't allocate axis structure";
- return TCL_ERROR;
- }
- location = (AxisLocation) type; /* For now, there's a 1-1
- * correspondence between axis
- * types and locations */
- axisPtr->type = type;
- axisPtr->location = location;
- axisPtr->interp = graphPtr->interp; /* Needed for Tcl_PrintDouble */
- axisPtr->showTicks = 1;
- axisPtr->reqSubTicks = 2;
- axisPtr->destroyProc = DestroyAxis;
- axisPtr->displayProc = DisplayAxis;
- axisPtr->layoutProc = LayoutAxis;
- axisPtr->printProc = PrintAxis;
-
- /*
- * The actual axis min and max can't be the same, so initializing
- * the previous limits to zero is Ok.
- */
- axisPtr->prevMin = axisPtr->prevMax = 0.0;
- axisPtr->mapped = ((location == BOTTOM_AXIS) || (location == LEFT_AXIS));
-
- graphPtr->axisArr[type] = (GraphAxis *)axisPtr;
- if (ConfigureAxis(graphPtr, axisPtr, 0, (char **)NULL, flags) != TCL_OK) {
- return TCL_ERROR;
- }
- return TCL_OK;
- }
-
- int
- Blt_AxisCmd(graphPtr, axis, argc, argv, flags)
- Graph *graphPtr;
- GraphAxis *axis;
- int argc;
- char **argv;
- int flags;
- {
- int result = TCL_ERROR;
- Axis *axisPtr = (Axis *)axis;
- Tcl_Interp *interp = graphPtr->interp;
- char c;
- int length;
- char *which;
-
- which = axisNames[axisPtr->type];
- if (argc < 3) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " ", which, "axis option ?args?\"", NULL);
- return TCL_ERROR;
- }
- c = argv[2][0];
- length = strlen(argv[2]);
-
- if ((c == 'c') && (strncmp(argv[2], "configure", length) == 0)) {
- if (argc < 3) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " ", which, "axis configure ?args?\"", NULL);
- return TCL_ERROR;
- }
- result = ConfigureAxis(graphPtr, axisPtr, argc - 3, argv + 3, flags);
- } else if ((c == 'l') && (strncmp(argv[2], "limits", length) == 0)) {
- result = GetAxisLimits(axisPtr, argc, argv);
- } else if ((c == 'i') && (strncmp(argv[2], "invtransform", length) == 0)) {
- result = InvTransformCoord(axisPtr, argc, argv);
- } else if ((c == 't') && (strncmp(argv[2], "transform", length) == 0)) {
- result = TransformCoord(axisPtr, argc, argv);
- } else {
- Tcl_AppendResult(interp, "bad ", which, "axis option \"", argv[2],
- "\": should be configure or limits", (char *)NULL);
- return TCL_ERROR;
- }
- return result;
- }
-